home *** CD-ROM | disk | FTP | other *** search
/ Power Programmierung / Power-Programmierung (Tewi)(1994).iso / magazine / drdobbs / 1991 / 02 / nelson / churn.c < prev    next >
C/C++ Source or Header  |  1990-05-24  |  8KB  |  252 lines

  1. /*
  2.  * Listing 13 -- churn.c
  3.  *
  4.  * This is a utility program used to test compression/decompression
  5.  * programs for accuracy, speed, and compression ratios.  Calling
  6.  * CHURN.EXE with a single argument will cause CHURN to compress then
  7.  * decompress every file in the specified directory tree, checking the
  8.  * compression ratio and the accuracy of the operation.  This is a good
  9.  * program to run overnight when you think your new algorithm works
  10.  * properly.
  11.  *
  12.  * This program will presently compile using TopSpeed C, Turbo C/C++,
  13.  * and QuickC.  The DOS directory structure-specific calls aren't
  14.  * portable to *NIX, VMS, etc.  They all need their own versions of
  15.  * of CHURN.C.
  16.  *
  17.  */
  18. #include <stdio.h>
  19. #include <stdlib.h>
  20. #include <string.h>
  21. #include <time.h>
  22. #include <process.h>
  23. #include <conio.h>
  24. #include <dos.h>
  25.  
  26. /*
  27.  * The findfirst and findnext functions operate nearly identically
  28.  * under TurboC and MSC.  The only difference is that the functions
  29.  * names, structures, and structure elements all have different names.
  30.  * I just create macros for these things and redefine them appropriately
  31.  * here.
  32.  */
  33. #ifdef __TURBOC__
  34.  
  35. #include <dir.h>
  36. #define FILE_INFO                 struct ffblk
  37. #define FIND_FIRST( name, info )  findfirst( ( name ), ( info ), FA_DIREC )
  38. #define FIND_NEXT( info )         findnext( ( info ) )
  39. #define FILE_IS_DIR( info )       ( ( info ).ff_attrib & FA_DIREC )
  40. #define FILE_NAME( info )         ( ( info ).ff_name )
  41.  
  42. #else
  43.  
  44. #define FILE_INFO                 struct find_t
  45. #define FIND_FIRST( name, info )  _dos_findfirst( ( name ), _A_SUBDIR, ( info ) )
  46. #define FIND_NEXT( info )         _dos_findnext( ( info ) )
  47. #define FILE_IS_DIR( info )       ( ( info ).attrib & _A_SUBDIR )
  48. #define FILE_NAME( info )         ( ( info ).name )
  49.  
  50. #endif
  51.  
  52. /*
  53.  * Some global variables.
  54.  */
  55. long total_input_bytes;
  56. long total_output_bytes;
  57.  
  58. /*
  59.  * Declarations for global routines.
  60.  */
  61. void churn_files( char *path );
  62. int file_is_already_compressed( char *name );
  63. void close_all_the_files( void );
  64. int compress( char *file_name );
  65.  
  66. /*
  67.  * main() doesn't have to do a whole lot in this program.  It
  68.  * reads in the command line to determine what the root directory
  69.  * to start looking at is, then it initializes the total byte counts
  70.  * and the start time.  It can then call churn_files(), which does all
  71.  * the work, then report on the statistics resulting from churn_files.
  72.  */
  73. void main( int argc, char *argv[] )
  74. {
  75.     time_t start_time;
  76.     time_t stop_time;
  77.     char argument[ 81 ];
  78.  
  79.     if ( argc < 2 )
  80.         strcpy( argument, "C:\\" );
  81.     else
  82.         strcpy( argument, argv[ 1 ] );
  83.     if ( argument[ strlen( argument ) - 1 ] != '\\' )
  84.        strcat( argument, "\\" );
  85.     total_input_bytes = 0L;
  86.     total_output_bytes = 0L;
  87.     time( &start_time );
  88.     churn_files( argument );
  89.     time( &stop_time );
  90.  
  91.     printf( "%f seconds\n", difftime( stop_time, start_time ) );
  92.     printf( "Total input bytes: %8ld: \n", total_input_bytes );
  93.     printf( "Total output bytes: %8ld: \n", total_output_bytes );
  94.     printf( "Reduction:%ld%%  ",
  95.             100L - ( ( 100L * total_output_bytes) / total_input_bytes ) );
  96.     printf( "Bits per byte: %f\n",
  97.             8.0 * total_output_bytes / total_input_bytes );
  98.  
  99. }
  100.  
  101. /*
  102.  * churn_files() is a routine that sits in a loop looking at
  103.  * files in the directory specified by its single argument, "path".
  104.  * As each file is looked at, one of three things happens.  If it
  105.  * is a normal file, and has a compressed extension name, like ".ZIP",
  106.  * the file is ignored.  If it is a normal file, and doesn't have a
  107.  * compressed extension name, it is compressed and decompressed by
  108.  * another routine.  Finally, if the file is a subdirectory,
  109.  * churn_files() is called recursively with the file name as its
  110.  * path argument.  This is one of those rare routines where recursion
  111.  * provides a way to truly simplify the task at hand.
  112.  */
  113. void churn_files( char *path )
  114. {
  115.     FILE_INFO file_info;
  116.     int result;
  117.     char full_name[ 81 ];
  118.  
  119.     strcpy( full_name, path );
  120.     strcat( full_name, "*.*" );
  121.     result = FIND_FIRST( full_name, &file_info );
  122.  
  123.     while ( result == 0 )
  124.     {
  125.         if ( kbhit() )
  126.         {
  127.             getch();
  128.             exit(0);
  129.         }
  130.         if ( FILE_IS_DIR( file_info ) )
  131.         {
  132.             if ( FILE_NAME( file_info )[ 0 ] != '.' )
  133.             {
  134.                 strcpy( full_name, path );
  135.                 strcat( full_name, FILE_NAME( file_info) );
  136.                 strcat( full_name, "\\" );
  137.                 churn_files( full_name );
  138.             }
  139.         }
  140.         else
  141.         {
  142.             strcpy( full_name, path );
  143.             strcat( full_name, FILE_NAME( file_info )  );
  144.             if ( !file_is_already_compressed( full_name ) )
  145.             {
  146.                 fprintf( stderr, "Testing %s\n", full_name );
  147.                 if ( !compress( full_name ) )
  148.                     fprintf( stderr, "Comparison failed!\n" );
  149.             }
  150.         }
  151.         result = FIND_NEXT( &file_info );
  152.     }
  153. }
  154.  
  155. /*
  156.  * The job of this routine is simply to check on the file
  157.  * whose name is passed as an argument.  The file extension is compared
  158.  * agains a list of standard extensions that are commonly used on
  159.  * compressed files.  If it matches one of these names, we assume it is
  160.  * compressed and return a TRUE, otherwise FALSE is returned.
  161.  */
  162. int file_is_already_compressed( char *name )
  163. {
  164.     char *extension;
  165.     static char *matches[]={ "ZIP", "ICE", "LZH", "ARC", "GIF", "PAK", NULL };
  166.     int i;
  167.  
  168.     extension=strchr( name, '.' );
  169.     if ( extension++ == NULL )
  170.         return( 0 );
  171.     i = 0;
  172.     while ( matches[ i ] != NULL )
  173.         if ( strcmpi( extension, matches[ i++ ] ) == 0 )
  174.             return( 1 );
  175.     return( 0 );
  176. }
  177.  
  178. /*
  179.  * This is the routine that does the majority of the work for
  180.  * this program.  It takes a file whose name is passed here.  It then
  181.  * compresses, then decompresses that file.  It then compares the file
  182.  * to the decompressed output, and reports on the results.
  183.  */
  184. FILE *input;
  185. FILE *output;
  186. FILE *compressed;
  187.  
  188. int compress( char *file_name )
  189. {
  190.     long new_size;
  191.     long old_size;
  192.     int c;
  193.  
  194.     printf( "%s\n", file_name );
  195.  
  196.     fflush( stdout );
  197.     spawnl( P_WAIT, "COMPRESS.EXE", "COMPRESS", "-f", file_name, NULL );
  198.     spawnl( P_WAIT, "EXPAND.EXE", "EXPAND", NULL );
  199.  
  200.     input = fopen( file_name, "rb" );
  201.     output = fopen( "test.out", "rb" );
  202.     compressed = fopen( "test.cmp", "rb" );
  203.  
  204.     if ( input == NULL || output == NULL || compressed == NULL )
  205.     {
  206.         close_all_the_files();
  207.         printf( "Failed, couldn't open file!\n" );
  208.         return( 0 );
  209.     }
  210.  
  211.     fseek( input, 0L, SEEK_END );
  212.     old_size = ftell( input );
  213.     fseek( input, 0L, SEEK_SET );
  214.     fseek( compressed, 0L, SEEK_END );
  215.     new_size = ftell( compressed );
  216.  
  217.     printf( "Old size: %8ld: ", old_size );
  218.     printf( "New size: %8ld\n", new_size );
  219.     if ( old_size == 0L )
  220.         old_size = 1L;
  221.     printf( "Reduction: %ld%%  ",
  222.             100L - ( ( 100L * new_size ) / old_size ) );
  223.     printf( "Bits per byte: %f\n", 8.0 * new_size / old_size );
  224.     total_input_bytes += old_size;
  225.     total_output_bytes += new_size;
  226.     do
  227.     {
  228.         c = getc( input );
  229.         if ( getc( output ) != c )
  230.         {
  231.             printf( "Failed comparison!\n" );
  232.             close_all_the_files();
  233.             return( 0 );
  234.         }
  235.     }
  236.     while ( c != EOF );
  237.     printf( "File compare passed.\n" );
  238.     close_all_the_files();
  239.     return( 1 );
  240. }
  241.  
  242. void close_all_the_files()
  243. {
  244.     if ( input != NULL )
  245.         fclose( input );
  246.     if ( output != NULL )
  247.         fclose( output );
  248.     if ( compressed != NULL )
  249.         fclose( compressed );
  250. }
  251.  
  252.